{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating Datasets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.data import Dataset\n",
    "from PIL import Image\n",
    "import torchvision.transforms.functional as TF\n",
    "import os\n",
    "import numpy as np\n",
    "\n",
    "import torch\n",
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "print(torch.__version__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CocoDataset(Dataset):\n",
    "    def __init__(self, path2listFile, transform=None, trans_params=None):\n",
    "        # get list of images\n",
    "        with open(path2listFile, \"r\") as file:\n",
    "            self.path2imgs = file.readlines()\n",
    "        \n",
    "        # get list of labels\n",
    "        self.path2labels = [\n",
    "            path.replace(\"images\", \"labels\").replace(\".png\", \".txt\").replace(\".jpg\", \".txt\")\n",
    "            for path in self.path2imgs]\n",
    "\n",
    "        self.trans_params = trans_params\n",
    "        self.transform = transform\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.path2imgs)\n",
    "    \n",
    "    def __getitem__(self, index):\n",
    "        path2img = self.path2imgs[index % len(self.path2imgs)].rstrip()\n",
    "\n",
    "        img = Image.open(path2img).convert('RGB')\n",
    "\n",
    "        path2label = self.path2labels[index % len(self.path2imgs)].rstrip()\n",
    "\n",
    "        labels= None\n",
    "        if os.path.exists(path2label):\n",
    "            labels = np.loadtxt(path2label).reshape(-1, 5)\n",
    "            \n",
    "        if self.transform:\n",
    "            img, labels = self.transform(img, labels, self.trans_params)\n",
    "\n",
    "        return img, labels, path2img    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "root_data=\"./data/coco\"\n",
    "path2trainList=os.path.join(root_data, \"trainvalno5k.txt\")\n",
    "coco_train = CocoDataset(path2trainList)\n",
    "print(len(coco_train))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "img, labels, path2img = coco_train[1] \n",
    "print(\"image size:\", img.size, type(img))\n",
    "print(\"labels shape:\", labels.shape, type(labels))\n",
    "print(\"labels \\n\", labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "path2valList=os.path.join(root_data, \"5k.txt\")\n",
    "coco_val = CocoDataset(path2valList, transform=None, trans_params=None)\n",
    "print(len(coco_val))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "img, labels, path2img = coco_val[7] \n",
    "print(\"image size:\", img.size, type(img))\n",
    "print(\"labels shape:\", labels.shape, type(labels))\n",
    "print(\"labels \\n\", labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pylab as plt\n",
    "import numpy as np\n",
    "from PIL import Image, ImageDraw, ImageFont\n",
    "from torchvision.transforms.functional import to_pil_image\n",
    "import random\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "path2cocoNames=\"./data/coco.names\"\n",
    "fp = open(path2cocoNames, \"r\")\n",
    "coco_names = fp.read().split(\"\\n\")[:-1]\n",
    "print(\"number of classese:\", len(coco_names))\n",
    "print(coco_names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rescale_bbox(bb,W,H):\n",
    "    x,y,w,h=bb\n",
    "    return [x*W, y*H, w*W, h*H]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "COLORS = np.random.randint(0, 255, size=(80, 3),dtype=\"uint8\")\n",
    "fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 16)\n",
    "def show_img_bbox(img,targets):\n",
    "    if torch.is_tensor(img):\n",
    "        img=to_pil_image(img)\n",
    "    if torch.is_tensor(targets):\n",
    "        targets=targets.numpy()[:,1:]\n",
    "        \n",
    "    W, H=img.size\n",
    "    draw = ImageDraw.Draw(img)\n",
    "    \n",
    "    for tg in targets:\n",
    "        id_=int(tg[0])\n",
    "        bbox=tg[1:]\n",
    "        bbox=rescale_bbox(bbox,W,H)\n",
    "        xc,yc,w,h=bbox\n",
    "        \n",
    "        color = [int(c) for c in COLORS[id_]]\n",
    "        name=coco_names[id_]\n",
    "        \n",
    "        draw.rectangle(((xc-w/2, yc-h/2), (xc+w/2, yc+h/2)),outline=tuple(color),width=3)\n",
    "        draw.text((xc-w/2,yc-h/2),name, font=fnt, fill=(255,255,255,0))\n",
    "    plt.imshow(np.array(img))        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(2)\n",
    "rnd_ind=np.random.randint(len(coco_train))\n",
    "img, labels, path2img = coco_train[rnd_ind] \n",
    "print(img.size, labels.shape)\n",
    "\n",
    "plt.rcParams['figure.figsize'] = (20, 10)\n",
    "show_img_bbox(img,labels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(0)\n",
    "rnd_ind=np.random.randint(len(coco_val))\n",
    "img, labels, path2img = coco_val[rnd_ind] \n",
    "print(img.size, labels.shape)\n",
    "\n",
    "plt.rcParams['figure.figsize'] = (20, 10)\n",
    "show_img_bbox(img,labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Transforming the Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pad_to_square(img, boxes, pad_value=0, normalized_labels=True):\n",
    "    w, h = img.size\n",
    "    w_factor, h_factor = (w,h) if normalized_labels else (1, 1)\n",
    "    \n",
    "    dim_diff = np.abs(h - w)\n",
    "    pad1= dim_diff // 2\n",
    "    pad2= dim_diff - pad1\n",
    "    \n",
    "    if h<=w:\n",
    "        left, top, right, bottom= 0, pad1, 0, pad2\n",
    "    else:\n",
    "        left, top, right, bottom= pad1, 0, pad2, 0\n",
    "    padding= (left, top, right, bottom)\n",
    "\n",
    "    img_padded = TF.pad(img, padding=padding, fill=pad_value)\n",
    "    w_padded, h_padded = img_padded.size\n",
    "            \n",
    "    x1 = w_factor * (boxes[:, 1] - boxes[:, 3] / 2)\n",
    "    y1 = h_factor * (boxes[:, 2] - boxes[:, 4] / 2)\n",
    "    x2 = w_factor * (boxes[:, 1] + boxes[:, 3] / 2)\n",
    "    y2 = h_factor * (boxes[:, 2] + boxes[:, 4] / 2)    \n",
    "    \n",
    "    x1 += padding[0] # left\n",
    "    y1 += padding[1] # top\n",
    "    x2 += padding[2] # right\n",
    "    y2 += padding[3] # bottom\n",
    "            \n",
    "    boxes[:, 1] = ((x1 + x2) / 2) / w_padded\n",
    "    boxes[:, 2] = ((y1 + y2) / 2) / h_padded\n",
    "    boxes[:, 3] *= w_factor / w_padded\n",
    "    boxes[:, 4] *= h_factor / h_padded\n",
    "\n",
    "    return img_padded, boxes    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def hflip(image, labels):\n",
    "    image = TF.hflip(image)\n",
    "    labels[:, 1] = 1.0 - labels[:, 1]\n",
    "    return image, labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def transformer(image, labels, params):\n",
    "    if params[\"pad2square\"] is True:\n",
    "        image,labels= pad_to_square(image, labels)\n",
    "    \n",
    "    image = TF.resize(image,params[\"target_size\"])\n",
    "\n",
    "    if random.random() < params[\"p_hflip\"]:\n",
    "        image,labels=hflip(image,labels)\n",
    "\n",
    "    image=TF.to_tensor(image)\n",
    "    targets = torch.zeros((len(labels), 6))\n",
    "    targets[:, 1:] = torch.from_numpy(labels)\n",
    "    \n",
    "    return image, targets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trans_params_train={\n",
    "    \"target_size\" : (416, 416),\n",
    "    \"pad2square\": True,\n",
    "    \"p_hflip\" : 1.0,\n",
    "    \"normalized_labels\": True,\n",
    "}\n",
    "coco_train= CocoDataset(path2trainList, \n",
    "                        transform=transformer,\n",
    "                         trans_params=trans_params_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(2)\n",
    "rnd_ind=np.random.randint(len(coco_train))\n",
    "img, targets, path2img = coco_train[rnd_ind] \n",
    "print(\"image shape:\", img.shape)\n",
    "print(\"labels shape:\", targets.shape) \n",
    "\n",
    "plt.rcParams['figure.figsize'] = (20, 10)\n",
    "COLORS = np.random.randint(0, 255, size=(80, 3),dtype=\"uint8\")\n",
    "show_img_bbox(img,targets)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trans_params_val={\n",
    "    \"target_size\" : (416, 416),\n",
    "    \"pad2square\": True,\n",
    "    \"p_hflip\" : 0.0,\n",
    "    \"normalized_labels\": True,\n",
    "}\n",
    "coco_val= CocoDataset(path2valList,\n",
    "                      transform=transformer,\n",
    "                      trans_params=trans_params_val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(0)\n",
    "rnd_ind=np.random.randint(len(coco_val))\n",
    "img, targets, path2img = coco_val[rnd_ind] \n",
    "print(\"image shape:\", img.shape)\n",
    "print(\"labels shape:\", targets.shape) \n",
    "\n",
    "plt.rcParams['figure.figsize'] = (20, 10)\n",
    "COLORS = np.random.randint(0, 255, size=(80, 3),dtype=\"uint8\")\n",
    "show_img_bbox(img,targets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Defining Data Loaders"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.data import DataLoader\n",
    "\n",
    "batch_size=8\n",
    "def collate_fn(batch):\n",
    "    imgs, targets, paths = list(zip(*batch))\n",
    "    \n",
    "    # Remove empty boxes\n",
    "    targets = [boxes for boxes in targets if boxes is not None]\n",
    "    \n",
    "    # set the sample index \n",
    "    for b_i, boxes in enumerate(targets):\n",
    "        boxes[:, 0] = b_i\n",
    "    targets = torch.cat(targets, 0)\n",
    "    imgs = torch.stack([img for img in imgs])\n",
    "    return imgs, targets, paths\n",
    "\n",
    "train_dl = DataLoader(\n",
    "        coco_train,\n",
    "        batch_size=batch_size,\n",
    "        shuffle=True,\n",
    "        num_workers=0,\n",
    "        pin_memory=True,\n",
    "        collate_fn=collate_fn,\n",
    "        )\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(0)\n",
    "for imgs_batch,tg_batch,path_batch in train_dl:\n",
    "    break\n",
    "print(imgs_batch.shape)\n",
    "print(tg_batch.shape,tg_batch.dtype)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val_dl = DataLoader(\n",
    "        coco_val,\n",
    "        batch_size=batch_size,\n",
    "        shuffle=False,\n",
    "        num_workers=0,\n",
    "        pin_memory=True,\n",
    "        collate_fn=collate_fn,\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.manual_seed(0)\n",
    "for imgs_batch,tg_batch,path_batch in val_dl:\n",
    "    break\n",
    "print(imgs_batch.shape)\n",
    "print(tg_batch.shape,tg_batch.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating YOLO-v3 Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'type': 'net',\n",
       "  'batch': '1',\n",
       "  'subdivisions': '1',\n",
       "  'width': '416',\n",
       "  'height': '416',\n",
       "  'channels': '3',\n",
       "  'momentum': '0.9',\n",
       "  'decay': '0.0005',\n",
       "  'angle': '0',\n",
       "  'saturation': '1.5',\n",
       "  'exposure': '1.5',\n",
       "  'hue': '.1',\n",
       "  'learning_rate': '0.001',\n",
       "  'burn_in': '1000',\n",
       "  'max_batches': '500200',\n",
       "  'policy': 'steps',\n",
       "  'steps': '400000,450000',\n",
       "  'scales': '.1,.1'},\n",
       " {'type': 'convolutional',\n",
       "  'batch_normalize': '1',\n",
       "  'filters': '32',\n",
       "  'size': '3',\n",
       "  'stride': '1',\n",
       "  'pad': '1',\n",
       "  'activation': 'leaky'}]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from myutils import parse_model_config\n",
    "\n",
    "path2config=\"./config/yolov3.cfg\"\n",
    "blocks_list = parse_model_config(path2config)\n",
    "blocks_list[:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating PyTorch modules"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ModuleList(\n",
      "  (0): Sequential(\n",
      "    (conv_0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_0): BatchNorm2d(32, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_0): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (1): Sequential(\n",
      "    (conv_1): Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "    (batch_norm_1): BatchNorm2d(64, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_1): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (2): Sequential(\n",
      "    (conv_2): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_2): BatchNorm2d(32, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_2): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (3): Sequential(\n",
      "    (conv_3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_3): BatchNorm2d(64, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_3): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (4): Sequential(\n",
      "    (shortcut_4): EmptyLayer()\n",
      "  )\n",
      "  (5): Sequential(\n",
      "    (conv_5): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "    (batch_norm_5): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_5): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (6): Sequential(\n",
      "    (conv_6): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_6): BatchNorm2d(64, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_6): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (7): Sequential(\n",
      "    (conv_7): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_7): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_7): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (8): Sequential(\n",
      "    (shortcut_8): EmptyLayer()\n",
      "  )\n",
      "  (9): Sequential(\n",
      "    (conv_9): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_9): BatchNorm2d(64, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_9): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (10): Sequential(\n",
      "    (conv_10): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_10): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_10): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (11): Sequential(\n",
      "    (shortcut_11): EmptyLayer()\n",
      "  )\n",
      "  (12): Sequential(\n",
      "    (conv_12): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "    (batch_norm_12): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_12): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (13): Sequential(\n",
      "    (conv_13): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_13): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_13): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (14): Sequential(\n",
      "    (conv_14): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_14): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_14): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (15): Sequential(\n",
      "    (shortcut_15): EmptyLayer()\n",
      "  )\n",
      "  (16): Sequential(\n",
      "    (conv_16): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_16): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_16): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (17): Sequential(\n",
      "    (conv_17): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_17): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_17): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (18): Sequential(\n",
      "    (shortcut_18): EmptyLayer()\n",
      "  )\n",
      "  (19): Sequential(\n",
      "    (conv_19): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_19): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_19): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (20): Sequential(\n",
      "    (conv_20): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_20): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_20): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (21): Sequential(\n",
      "    (shortcut_21): EmptyLayer()\n",
      "  )\n",
      "  (22): Sequential(\n",
      "    (conv_22): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_22): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_22): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (23): Sequential(\n",
      "    (conv_23): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_23): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_23): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (24): Sequential(\n",
      "    (shortcut_24): EmptyLayer()\n",
      "  )\n",
      "  (25): Sequential(\n",
      "    (conv_25): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_25): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_25): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (26): Sequential(\n",
      "    (conv_26): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_26): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_26): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (27): Sequential(\n",
      "    (shortcut_27): EmptyLayer()\n",
      "  )\n",
      "  (28): Sequential(\n",
      "    (conv_28): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_28): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_28): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (29): Sequential(\n",
      "    (conv_29): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_29): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_29): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (30): Sequential(\n",
      "    (shortcut_30): EmptyLayer()\n",
      "  )\n",
      "  (31): Sequential(\n",
      "    (conv_31): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_31): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_31): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (32): Sequential(\n",
      "    (conv_32): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_32): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_32): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (33): Sequential(\n",
      "    (shortcut_33): EmptyLayer()\n",
      "  )\n",
      "  (34): Sequential(\n",
      "    (conv_34): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_34): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_34): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (35): Sequential(\n",
      "    (conv_35): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_35): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_35): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (36): Sequential(\n",
      "    (shortcut_36): EmptyLayer()\n",
      "  )\n",
      "  (37): Sequential(\n",
      "    (conv_37): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "    (batch_norm_37): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_37): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (38): Sequential(\n",
      "    (conv_38): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_38): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_38): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (39): Sequential(\n",
      "    (conv_39): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_39): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_39): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (40): Sequential(\n",
      "    (shortcut_40): EmptyLayer()\n",
      "  )\n",
      "  (41): Sequential(\n",
      "    (conv_41): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_41): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_41): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (42): Sequential(\n",
      "    (conv_42): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_42): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_42): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (43): Sequential(\n",
      "    (shortcut_43): EmptyLayer()\n",
      "  )\n",
      "  (44): Sequential(\n",
      "    (conv_44): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_44): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_44): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (45): Sequential(\n",
      "    (conv_45): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_45): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_45): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (46): Sequential(\n",
      "    (shortcut_46): EmptyLayer()\n",
      "  )\n",
      "  (47): Sequential(\n",
      "    (conv_47): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_47): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_47): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (48): Sequential(\n",
      "    (conv_48): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_48): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_48): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (49): Sequential(\n",
      "    (shortcut_49): EmptyLayer()\n",
      "  )\n",
      "  (50): Sequential(\n",
      "    (conv_50): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_50): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_50): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (51): Sequential(\n",
      "    (conv_51): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_51): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_51): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (52): Sequential(\n",
      "    (shortcut_52): EmptyLayer()\n",
      "  )\n",
      "  (53): Sequential(\n",
      "    (conv_53): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_53): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_53): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (54): Sequential(\n",
      "    (conv_54): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_54): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_54): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (55): Sequential(\n",
      "    (shortcut_55): EmptyLayer()\n",
      "  )\n",
      "  (56): Sequential(\n",
      "    (conv_56): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_56): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_56): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (57): Sequential(\n",
      "    (conv_57): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_57): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_57): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (58): Sequential(\n",
      "    (shortcut_58): EmptyLayer()\n",
      "  )\n",
      "  (59): Sequential(\n",
      "    (conv_59): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_59): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_59): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (60): Sequential(\n",
      "    (conv_60): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_60): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_60): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (61): Sequential(\n",
      "    (shortcut_61): EmptyLayer()\n",
      "  )\n",
      "  (62): Sequential(\n",
      "    (conv_62): Conv2d(512, 1024, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n",
      "    (batch_norm_62): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_62): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (63): Sequential(\n",
      "    (conv_63): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_63): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_63): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (64): Sequential(\n",
      "    (conv_64): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_64): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_64): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (65): Sequential(\n",
      "    (shortcut_65): EmptyLayer()\n",
      "  )\n",
      "  (66): Sequential(\n",
      "    (conv_66): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_66): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_66): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (67): Sequential(\n",
      "    (conv_67): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_67): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_67): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (68): Sequential(\n",
      "    (shortcut_68): EmptyLayer()\n",
      "  )\n",
      "  (69): Sequential(\n",
      "    (conv_69): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_69): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_69): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (70): Sequential(\n",
      "    (conv_70): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_70): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_70): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (71): Sequential(\n",
      "    (shortcut_71): EmptyLayer()\n",
      "  )\n",
      "  (72): Sequential(\n",
      "    (conv_72): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_72): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_72): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (73): Sequential(\n",
      "    (conv_73): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_73): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_73): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (74): Sequential(\n",
      "    (shortcut_74): EmptyLayer()\n",
      "  )\n",
      "  (75): Sequential(\n",
      "    (conv_75): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_75): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_75): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (76): Sequential(\n",
      "    (conv_76): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_76): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_76): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (77): Sequential(\n",
      "    (conv_77): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_77): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_77): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (78): Sequential(\n",
      "    (conv_78): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_78): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_78): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (79): Sequential(\n",
      "    (conv_79): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_79): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_79): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (80): Sequential(\n",
      "    (conv_80): Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_80): BatchNorm2d(1024, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_80): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (81): Sequential(\n",
      "    (conv_81): Conv2d(1024, 255, kernel_size=(1, 1), stride=(1, 1))\n",
      "  )\n",
      "  (82): Sequential(\n",
      "    (yolo_82): YOLOLayer()\n",
      "  )\n",
      "  (83): Sequential(\n",
      "    (route_83): EmptyLayer()\n",
      "  )\n",
      "  (84): Sequential(\n",
      "    (conv_84): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_84): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_84): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (85): Sequential(\n",
      "    (upsample_85): Upsample(scale_factor=2.0, mode=nearest)\n",
      "  )\n",
      "  (86): Sequential(\n",
      "    (route_86): EmptyLayer()\n",
      "  )\n",
      "  (87): Sequential(\n",
      "    (conv_87): Conv2d(768, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_87): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_87): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (88): Sequential(\n",
      "    (conv_88): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_88): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_88): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (89): Sequential(\n",
      "    (conv_89): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_89): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_89): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (90): Sequential(\n",
      "    (conv_90): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_90): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_90): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (91): Sequential(\n",
      "    (conv_91): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_91): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_91): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (92): Sequential(\n",
      "    (conv_92): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_92): BatchNorm2d(512, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_92): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (93): Sequential(\n",
      "    (conv_93): Conv2d(512, 255, kernel_size=(1, 1), stride=(1, 1))\n",
      "  )\n",
      "  (94): Sequential(\n",
      "    (yolo_94): YOLOLayer()\n",
      "  )\n",
      "  (95): Sequential(\n",
      "    (route_95): EmptyLayer()\n",
      "  )\n",
      "  (96): Sequential(\n",
      "    (conv_96): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_96): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_96): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (97): Sequential(\n",
      "    (upsample_97): Upsample(scale_factor=2.0, mode=nearest)\n",
      "  )\n",
      "  (98): Sequential(\n",
      "    (route_98): EmptyLayer()\n",
      "  )\n",
      "  (99): Sequential(\n",
      "    (conv_99): Conv2d(384, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_99): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_99): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (100): Sequential(\n",
      "    (conv_100): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_100): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_100): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (101): Sequential(\n",
      "    (conv_101): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_101): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_101): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (102): Sequential(\n",
      "    (conv_102): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_102): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_102): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (103): Sequential(\n",
      "    (conv_103): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)\n",
      "    (batch_norm_103): BatchNorm2d(128, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_103): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (104): Sequential(\n",
      "    (conv_104): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n",
      "    (batch_norm_104): BatchNorm2d(256, eps=1e-05, momentum=0.9, affine=True, track_running_stats=True)\n",
      "    (leaky_104): LeakyReLU(negative_slope=0.1)\n",
      "  )\n",
      "  (105): Sequential(\n",
      "    (conv_105): Conv2d(256, 255, kernel_size=(1, 1), stride=(1, 1))\n",
      "  )\n",
      "  (106): Sequential(\n",
      "    (yolo_106): YOLOLayer()\n",
      "  )\n",
      ")\n",
      "{'type': 'net', 'batch': '1', 'subdivisions': '1', 'width': '416', 'height': '416', 'channels': '3', 'momentum': '0.9', 'decay': '0.0005', 'angle': '0', 'saturation': '1.5', 'exposure': '1.5', 'hue': '.1', 'learning_rate': '0.001', 'burn_in': '1000', 'max_batches': '500200', 'policy': 'steps', 'steps': '400000,450000', 'scales': '.1,.1'}\n"
     ]
    }
   ],
   "source": [
    "from myutils import create_layers\n",
    "\n",
    "hy_pa, m_l= create_layers(blocks_list)\n",
    "print(m_l)\n",
    "print(hy_pa)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Defining Darknet Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Darknet(nn.Module):\n",
    "    def __init__(self, config_path, img_size=416):\n",
    "        super(Darknet, self).__init__()\n",
    "        self.blocks_list = parse_model_config(config_path)\n",
    "        self.hyperparams, self.module_list = create_layers(self.blocks_list)\n",
    "        self.img_size = img_size\n",
    "        \n",
    "    def forward(self, x):\n",
    "        img_dim = x.shape[2]\n",
    "        layer_outputs, yolo_outputs = [], []\n",
    "        \n",
    "        for block, module in zip(self.blocks_list[1:], self.module_list):\n",
    "            if block[\"type\"] in [\"convolutional\", \"upsample\", \"maxpool\"]:\n",
    "                x = module(x)        \n",
    "                \n",
    "                \n",
    "            elif block[\"type\"] == \"shortcut\":\n",
    "                layer_ind = int(block[\"from\"])\n",
    "                x = layer_outputs[-1] + layer_outputs[layer_ind]\n",
    "            elif block[\"type\"] == \"yolo\":\n",
    "                x= module[0](x)\n",
    "                yolo_outputs.append(x)\n",
    "            elif block[\"type\"] == \"route\":\n",
    "                x = torch.cat([layer_outputs[int(l_i)] \n",
    "                               for l_i in block[\"layers\"].split(\",\")], 1)\n",
    "            layer_outputs.append(x)\n",
    "        yolo_out_cat = torch.cat(yolo_outputs, 1)\n",
    "        return yolo_out_cat, yolo_outputs        \n",
    "    \n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Darknet(path2config).to(device)\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(next(model.parameters()).device)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummy_img=torch.rand(1,3,416,416).to(device)\n",
    "with torch.no_grad():\n",
    "    dummy_out_cat, dummy_out=model.forward(dummy_img)\n",
    "    print(dummy_out_cat.shape)\n",
    "    print(dummy_out[0].shape,dummy_out[1].shape,dummy_out[2].shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Defining the Loss Function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_loss_batch(output,targets, params_loss, opt=None):\n",
    "    ignore_thres=params_loss[\"ignore_thres\"]\n",
    "    scaled_anchors= params_loss[\"scaled_anchors\"]    \n",
    "    mse_loss= params_loss[\"mse_loss\"]\n",
    "    bce_loss= params_loss[\"bce_loss\"]\n",
    "    \n",
    "    num_yolos=params_loss[\"num_yolos\"]\n",
    "    num_anchors= params_loss[\"num_anchors\"]\n",
    "    obj_scale= params_loss[\"obj_scale\"]\n",
    "    noobj_scale= params_loss[\"noobj_scale\"]\n",
    "    \n",
    "    loss=0.0\n",
    "    for yolo_ind in range(num_yolos):\n",
    "        yolo_out=output[yolo_ind]\n",
    "        batch_size, num_bbxs, _=yolo_out.shape\n",
    "        \n",
    "        # get grid size\n",
    "        gz_2=num_bbxs/num_anchors\n",
    "        grid_size=int(np.sqrt(gz_2))\n",
    "        \n",
    "        yolo_out=yolo_out.view(batch_size,num_anchors,grid_size,grid_size,-1)\n",
    "        \n",
    "        pred_boxes=yolo_out[:,:,:,:,:4]\n",
    "        x,y,w,h= transform_bbox(pred_boxes, scaled_anchors[yolo_ind])\n",
    "        pred_conf=yolo_out[:,:,:,:,4]\n",
    "        pred_cls_prob=yolo_out[:,:,:,:,5:]\n",
    "        \n",
    "        yolo_targets = get_yolo_targets({\n",
    "                                            \"pred_cls_prob\": pred_cls_prob,\n",
    "                                            \"pred_boxes\":pred_boxes,    \n",
    "                                            \"targets\": targets,    \n",
    "                                            \"anchors\": scaled_anchors[yolo_ind],    \n",
    "                                            \"ignore_thres\": ignore_thres,\n",
    "                                        }) \n",
    "        \n",
    "        obj_mask=yolo_targets[\"obj_mask\"]        \n",
    "        noobj_mask=yolo_targets[\"noobj_mask\"]            \n",
    "        tx=yolo_targets[\"tx\"]                \n",
    "        ty=yolo_targets[\"ty\"]                    \n",
    "        tw=yolo_targets[\"tw\"]                        \n",
    "        th=yolo_targets[\"th\"]                            \n",
    "        tcls=yolo_targets[\"tcls\"]                                \n",
    "        t_conf=yolo_targets[\"t_conf\"]\n",
    "        \n",
    "        loss_x = mse_loss(x[obj_mask], tx[obj_mask])\n",
    "        loss_y = mse_loss(y[obj_mask], ty[obj_mask])\n",
    "        loss_w = mse_loss(w[obj_mask], tw[obj_mask])\n",
    "        loss_h = mse_loss(h[obj_mask], th[obj_mask])\n",
    "        \n",
    "        loss_conf_obj = bce_loss(pred_conf[obj_mask], t_conf[obj_mask])\n",
    "        loss_conf_noobj = bce_loss(pred_conf[noobj_mask], t_conf[noobj_mask])\n",
    "        loss_conf = obj_scale * loss_conf_obj + noobj_scale * loss_conf_noobj\n",
    "        loss_cls = bce_loss(pred_cls_prob[obj_mask], tcls[obj_mask])\n",
    "        loss += loss_x + loss_y + loss_w + loss_h + loss_conf + loss_cls\n",
    "        \n",
    "    if opt is not None:\n",
    "        opt.zero_grad()\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "        \n",
    "    return loss.item()        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def transform_bbox(bbox, anchors):\n",
    "    x=bbox[:,:,:,:,0]\n",
    "    y=bbox[:,:,:,:,1]\n",
    "    w=bbox[:,:,:,:,2]\n",
    "    h=bbox[:,:,:,:,3]\n",
    "    anchor_w = anchors[:, 0].view((1, 3, 1, 1))\n",
    "    anchor_h = anchors[:, 1].view((1, 3, 1, 1))       \n",
    "    \n",
    "    x=x-x.floor()\n",
    "    y=y-y.floor()\n",
    "    w= torch.log(w / anchor_w + 1e-16)\n",
    "    h= torch.log(h / anchor_h + 1e-16)\n",
    "    return x, y, w, h"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_yolo_targets(params):\n",
    "    pred_boxes=params[\"pred_boxes\"]\n",
    "    pred_cls_prob=params[\"pred_cls_prob\"]\n",
    "    target=params[\"targets\"]\n",
    "    anchors=params[\"anchors\"] \n",
    "    ignore_thres=params[\"ignore_thres\"] \n",
    "\n",
    "    batch_size = pred_boxes.size(0)\n",
    "    num_anchors = pred_boxes.size(1)\n",
    "    grid_size = pred_boxes.size(2)\n",
    "    num_cls = pred_cls_prob.size(-1)\n",
    "    \n",
    "    \n",
    "    sizeT=batch_size, num_anchors, grid_size, grid_size\n",
    "    obj_mask = torch.zeros(sizeT,device=device,dtype=torch.uint8)\n",
    "    noobj_mask = torch.ones(sizeT,device=device,dtype=torch.uint8)\n",
    "    tx = torch.zeros(sizeT, device=device, dtype=torch.float32)\n",
    "    ty= torch.zeros(sizeT, device=device, dtype=torch.float32)\n",
    "    tw= torch.zeros(sizeT, device=device, dtype=torch.float32)\n",
    "    th= torch.zeros(sizeT, device=device, dtype=torch.float32)\n",
    "    \n",
    "    sizeT=batch_size, num_anchors, grid_size, grid_size, num_cls\n",
    "    tcls= torch.zeros(sizeT, device=device, dtype=torch.float32)\n",
    "    \n",
    "    target_bboxes = target[:, 2:] * grid_size\n",
    "    t_xy = target_bboxes[:, :2]\n",
    "    t_wh = target_bboxes[:, 2:]\n",
    "    t_x, t_y = t_xy.t()\n",
    "    t_w, t_h = t_wh.t()\n",
    "\n",
    "    grid_i, grid_j = t_xy.long().t()\n",
    "    \n",
    "    iou_with_anchors=[get_iou_WH(anchor, t_wh) for anchor in anchors]\n",
    "    iou_with_anchors = torch.stack(iou_with_anchors)\n",
    "    best_iou_wa, best_anchor_ind = iou_with_anchors.max(0)\n",
    "    \n",
    "    batch_inds, target_labels = target[:, :2].long().t()\n",
    "    obj_mask[batch_inds, best_anchor_ind, grid_j, grid_i] = 1\n",
    "    noobj_mask[batch_inds, best_anchor_ind, grid_j, grid_i] = 0\n",
    "\n",
    "    for ind, iou_wa in enumerate(iou_with_anchors.t()):\n",
    "        noobj_mask[batch_inds[ind], iou_wa > ignore_thres, grid_j[ind], grid_i[ind]] = 0\n",
    "        \n",
    "        \n",
    "    tx[batch_inds, best_anchor_ind, grid_j, grid_i] = t_x - t_x.floor()\n",
    "    ty[batch_inds, best_anchor_ind, grid_j, grid_i] = t_y - t_y.floor()\n",
    "    \n",
    "\n",
    "    anchor_w=anchors[best_anchor_ind][:, 0]\n",
    "    tw[batch_inds, best_anchor_ind, grid_j, grid_i] = torch.log(t_w / anchor_w + 1e-16)\n",
    "    \n",
    "    anchor_h=anchors[best_anchor_ind][:, 1]\n",
    "    th[batch_inds, best_anchor_ind, grid_j, grid_i] = torch.log(t_h / anchor_h + 1e-16)\n",
    "    \n",
    "    tcls[batch_inds, best_anchor_ind, grid_j, grid_i, target_labels] = 1\n",
    "    \n",
    "    output={\n",
    "        \"obj_mask\" : obj_mask,\n",
    "        \"noobj_mask\" : noobj_mask,\n",
    "        \"tx\": tx,\n",
    "        \"ty\": ty,\n",
    "        \"tw\": tw,\n",
    "        \"th\": th,\n",
    "        \"tcls\": tcls,\n",
    "        \"t_conf\": obj_mask.float(),\n",
    "    }\n",
    "    return output    \n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_iou_WH(wh1, wh2):\n",
    "    wh2 = wh2.t()\n",
    "    w1, h1 = wh1[0], wh1[1]\n",
    "    w2, h2 = wh2[0], wh2[1]\n",
    "    inter_area = torch.min(w1, w2) * torch.min(h1, h2)\n",
    "    union_area = (w1 * h1 + 1e-16) + w2 * h2 - inter_area\n",
    "    return inter_area / union_area"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training the Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loss_epoch(model,params_loss,dataset_dl,sanity_check=False,opt=None):\n",
    "    running_loss=0.0\n",
    "    len_data=len(dataset_dl.dataset)\n",
    "    running_metrics= {}\n",
    "    \n",
    "    for xb, yb,_ in dataset_dl:\n",
    "        yb=yb.to(device)\n",
    "        _,output=model(xb.to(device))\n",
    "        loss_b=get_loss_batch(output,yb, params_loss,opt)\n",
    "        running_loss+=loss_b\n",
    "        if sanity_check is True:\n",
    "            break \n",
    "    loss=running_loss/float(len_data)\n",
    "    return loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import copy\n",
    "def train_val(model, params):\n",
    "    num_epochs=params[\"num_epochs\"]\n",
    "    params_loss=params[\"params_loss\"]\n",
    "    opt=params[\"optimizer\"]\n",
    "    train_dl=params[\"train_dl\"]\n",
    "    val_dl=params[\"val_dl\"]\n",
    "    sanity_check=params[\"sanity_check\"]\n",
    "    lr_scheduler=params[\"lr_scheduler\"]\n",
    "    path2weights=params[\"path2weights\"]\n",
    "    \n",
    "    \n",
    "    loss_history={\n",
    "        \"train\": [],\n",
    "        \"val\": [],\n",
    "    }\n",
    "    best_model_wts = copy.deepcopy(model.state_dict())\n",
    "    best_loss=float('inf') \n",
    "    \n",
    "    for epoch in range(num_epochs):\n",
    "        current_lr=get_lr(opt)\n",
    "        print('Epoch {}/{}, current lr={}'.format(epoch, num_epochs - 1, current_lr)) \n",
    "        model.train()\n",
    "        train_loss=loss_epoch(model,params_loss,train_dl,sanity_check,opt)\n",
    "        loss_history[\"train\"].append(train_loss)\n",
    "        print(\"train loss: %.6f\" %(train_loss))    \n",
    "        \n",
    "        model.eval()\n",
    "        with torch.no_grad():\n",
    "            val_loss=loss_epoch(model,params_loss,val_dl,sanity_check)\n",
    "        loss_history[\"val\"].append(val_loss)\n",
    "        print(\"val loss: %.6f\" %(val_loss))\n",
    "        \n",
    "        \n",
    "        if val_loss < best_loss:\n",
    "            best_loss = val_loss\n",
    "            best_model_wts = copy.deepcopy(model.state_dict())\n",
    "            torch.save(model.state_dict(), path2weights)\n",
    "            print(\"Copied best model weights!\")\n",
    "            \n",
    "        lr_scheduler.step(val_loss)\n",
    "        if current_lr != get_lr(opt):\n",
    "            print(\"Loading best model weights!\")\n",
    "            model.load_state_dict(best_model_wts) \n",
    "        print(\"-\"*10) \n",
    "    model.load_state_dict(best_model_wts)\n",
    "    return model, loss_history            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_lr(opt):\n",
    "    for param_group in opt.param_groups:\n",
    "        return param_group['lr']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch import optim\n",
    "from torch.optim.lr_scheduler import ReduceLROnPlateau\n",
    "\n",
    "opt = optim.Adam(model.parameters(), lr=1e-3)\n",
    "lr_scheduler = ReduceLROnPlateau(opt, mode='min',factor=0.5, patience=20,verbose=1)\n",
    "\n",
    "path2models= \"./models/\"\n",
    "if not os.path.exists(path2models):\n",
    "        os.mkdir(path2models)\n",
    "        \n",
    "scaled_anchors=[model.module_list[82][0].scaled_anchors,\n",
    "                model.module_list[94][0].scaled_anchors,\n",
    "                model.module_list[106][0].scaled_anchors]        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mse_loss = nn.MSELoss(reduction=\"sum\")\n",
    "bce_loss = nn.BCELoss(reduction=\"sum\")\n",
    "params_loss={\n",
    "    \"scaled_anchors\" : scaled_anchors,\n",
    "    \"ignore_thres\": 0.5,\n",
    "    \"mse_loss\": mse_loss,\n",
    "    \"bce_loss\": bce_loss,\n",
    "    \"num_yolos\": 3,\n",
    "    \"num_anchors\": 3,\n",
    "    \"obj_scale\": 1,\n",
    "    \"noobj_scale\": 100,\n",
    "} "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "params_train={\n",
    "    \"num_epochs\": 5,\n",
    "    \"optimizer\": opt,\n",
    "    \"params_loss\": params_loss,\n",
    "    \"train_dl\": train_dl,\n",
    "    \"val_dl\": val_dl,\n",
    "    \"sanity_check\": True,\n",
    "    \"lr_scheduler\": lr_scheduler,\n",
    "    \"path2weights\": path2models+\"weights.pt\",\n",
    "}\n",
    "model,loss_hist=train_val(model,params_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploying the Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "path2weights=\"./models/weights.pt\"\n",
    "model.load_state_dict(torch.load(path2weights))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "img,tg,_=coco_val[4]\n",
    "print(img.shape)\n",
    "print(tg.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show_img_bbox(img,tg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    out,_=model(img.unsqueeze(0).to(device))\n",
    "print(out.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def NonMaxSuppression(bbox_pred, obj_threshold=0.5, nms_thres=0.5):\n",
    "    bbox_pred[..., :4] = xywh2xyxy(bbox_pred[..., :4])\n",
    "    output = [None] * len(bbox_pred)\n",
    "    \n",
    "    for ind, bb_pr in enumerate(bbox_pred):\n",
    "        bb_pr = bb_pr[bb_pr[:, 4] >= obj_threshold]\n",
    "        \n",
    "        if not bb_pr.size(0):\n",
    "            continue\n",
    "            \n",
    "        score = bb_pr[:, 4] * bb_pr[:, 5:].max(1)[0]\n",
    "        bb_pr = bb_pr[(-score).argsort()]\n",
    "        \n",
    "        cls_probs, cls_preds = bb_pr[:, 5:].max(1, keepdim=True)\n",
    "        detections = torch.cat((bb_pr[:, :5], \n",
    "                                cls_probs.float(), \n",
    "                                cls_preds.float()), 1)\n",
    "        \n",
    "        bbox_nms = []\n",
    "        while detections.size(0):\n",
    "            high_iou_inds = bbox_iou(detections[0, :4].unsqueeze(0), \n",
    "                                     detections[:, :4]) > nms_thres\n",
    "            \n",
    "            cls_match_inds = detections[0, -1] == detections[:, -1]\n",
    "            supp_inds = high_iou_inds & cls_match_inds\n",
    "            \n",
    "            ww = detections[supp_inds, 4]\n",
    "            detections[0, :4] = (ww * detections[supp_inds, :4]).sum(0) / ww.sum()\n",
    "            \n",
    "            bbox_nms += [detections[0]]\n",
    "            detections = detections[~supp_inds]\n",
    "            \n",
    "        if bbox_nms:\n",
    "            output[ind] = torch.stack(bbox_nms)\n",
    "            output[ind]=xyxyh2xywh(output[ind])\n",
    "    return output            "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def xywh2xyxy(xywh):\n",
    "    xyxy = xywh.new(xywh.shape)\n",
    "    xyxy[..., 0] = xywh[..., 0] - xywh[..., 2] / 2.0\n",
    "    xyxy[..., 1] = xywh[..., 1] - xywh[..., 3] / 2.0\n",
    "    xyxy[..., 2] = xywh[..., 0] + xywh[..., 2] / 2.0\n",
    "    xyxy[..., 3] = xywh[..., 1] + xywh[..., 3] / 2.0\n",
    "    return xyxy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def xyxyh2xywh(xyxy, image_size=416):\n",
    "    xywh = torch.zeros(xyxy.shape[0],6)\n",
    "    xywh[:,2] = (xyxy[:, 0] + xyxy[:, 2]) / 2./img_size\n",
    "    xywh[:,3] = (xyxy[:, 1] + xyxy[:, 3]) / 2./img_size\n",
    "    xywh[:,5] = (xyxy[:, 2] - xyxy[:, 0])/img_size \n",
    "    xywh[:,4] = (xyxy[:, 3] - xyxy[:, 1])/img_size\n",
    "    xywh[:,1]= xyxy[:,6]    \n",
    "    return xywh"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def bbox_iou(box1, box2):\n",
    "    b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3]\n",
    "    b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]\n",
    "    inter_rect_x1 = torch.max(b1_x1, b2_x1)\n",
    "    inter_rect_y1 = torch.max(b1_y1, b2_y1)\n",
    "    inter_rect_x2 = torch.min(b1_x2, b2_x2)\n",
    "    inter_rect_y2 = torch.min(b1_y2, b2_y2)\n",
    "    inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1 + 1, min=0) \\\n",
    "                    *torch.clamp(inter_rect_y2 - inter_rect_y1 + 1, min=0)\n",
    "    b1_area = (b1_x2 - b1_x1 + 1.0) * (b1_y2 - b1_y1 + 1.0)\n",
    "    b2_area = (b2_x2 - b2_x1 + 1.0) * (b2_y2 - b2_y1 + 1.0)\n",
    "    iou = inter_area / (b1_area + b2_area - inter_area + 1e-16)\n",
    "    return iou"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "img_size=416\n",
    "out_nms=NonMaxSuppression(out.cpu())\n",
    "print (out_nms[0].shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show_img_bbox(img,out_nms[0])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
